/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.text; import java.io.*; import java.util.*; import java.lang.ref.*; import java.util.WeakHashMap; import javax.swing.text.*; import javax.swing.event.*; import org.openide.*; import org.openide.loaders.*; import org.openide.util.WeakListener; import org.openide.util.Task; import org.openide.util.RequestProcessor; /** Implementation of a line in a {@link StyledDocument}. * One object * of this class represents a line in the document by holding * a {@link PositionRef}, which can represent a position in an open or * closed document. * * @author Jaroslav Tulach */ public abstract class DocumentLine extends Line { /** reference to one position on the line */ protected PositionRef pos; /** is breakpoint there - presistent state */ private boolean breakpoint; /** error line - transient state */ private transient boolean error; /** current line - transient state */ private transient boolean current; /** listener for changes of state of the document */ private transient Listener listener; /** weak document listener assigned to the document or null */ private transient DocumentListener docL; /** weak map that assignes to editor supports whether they have current or error line * selected. (EditorSupport, DocumentLine[2]), where Line[0] is current and Line[1] is error */ private static WeakHashMap assigned = new WeakHashMap (5); static final long serialVersionUID =3213776466939427487L; /** Constructor. * @param obj data object we belong to * @param pos position on the line */ public DocumentLine (DataObject obj, PositionRef pos) { super (obj); this.pos = pos; } /** Init listeners */ private void init () { listener = new Listener (); pos.getEditorSupport ().addChangeListener (WeakListener.change (listener, pos.getEditorSupport ())); } /* Get the line number. * The number may change if the * text is modified. * * @return Returns current line number. */ public int getLineNumber () { try { return pos.getLine (); } catch (IOException ex) { // what else? return 0; } } /* Shows the line. * @param kind one of SHOW_XXX constants. * @column the column of this line which should be selected */ public abstract void show(int kind, int column); /* Sets the breakpoint. */ public void setBreakpoint(boolean b) { if (breakpoint != b) { breakpoint = b; refreshState (); } } /* Tests if the breakpoint is set. */ public boolean isBreakpoint () { return breakpoint; } /* Marks the error. */ public void markError () { DocumentLine previous = registerLine (0, this); if (previous != null) { previous.error = false; previous.refreshState (); } error = true; refreshState (); } /* Unmarks error at this line. */ public void unmarkError () { error = false; registerLine (1, null); refreshState (); } /* Marks this line as current. */ public void markCurrentLine () { DocumentLine previous = registerLine (0, this); if (previous != null) { previous.current = false; previous.refreshState (); } current = true; refreshState (); } /* Unmarks this line as current. */ public void unmarkCurrentLine () { current = false; registerLine (0, null); refreshState (); } /** Refreshes the current line. */ synchronized void refreshState () { StyledDocument doc = pos.getEditorSupport ().getDocument (); if (doc != null) { // the document is in memory, mark the state if (docL != null) { doc.removeDocumentListener (docL); } // error line if (error) { NbDocument.markError (doc, pos.getOffset ()); doc.addDocumentListener (docL = WeakListener.document (listener, doc)); return; } // current line if (current) { NbDocument.markCurrent (doc, pos.getOffset ()); return; } // breakpoint line if (breakpoint) { NbDocument.markBreakpoint (doc, pos.getOffset ()); return; } NbDocument.markNormal (doc, pos.getOffset ()); return; } } public int hashCode () { return pos.getEditorSupport ().hashCode (); } public boolean equals (Object o) { if (o instanceof DocumentLine) { DocumentLine dl = (DocumentLine)o; if (dl.pos.getEditorSupport () == pos.getEditorSupport ()) { return dl.getLineNumber () == getLineNumber (); } } return false; } // // Work with global hash table // /** Register this line as the one stored * under indx-index (0 = current, 1 = error). * * @param indx index to register * @param line value to add (this or null) * @return the previous value */ private DocumentLine registerLine (int indx, DocumentLine line) { DocumentLine prev; EditorSupport es = pos.getEditorSupport (); DocumentLine[] arr = (DocumentLine[])assigned.get (es); if (arr != null) { // remember the previous prev = arr[indx]; } else { // create new array arr = new DocumentLine[2]; assigned.put (es, arr); prev = null; } arr[indx] = line; return prev; } // // Serialization // /** Write fields. */ private void writeObject (ObjectOutputStream oos) throws IOException { // do not do default read/write object oos.writeObject (pos); oos.writeBoolean (breakpoint); } /** Read important fields. */ private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { pos = (PositionRef)ois.readObject (); setBreakpoint (ois.readBoolean ()); } /** Register line. */ Object readResolve() throws ObjectStreamException { return Set.registerLine (this); } /** Listener to Position.Ref manager's state (in memory, on disk). */ private final class Listener implements ChangeListener, DocumentListener { public void stateChanged (ChangeEvent ev) { refreshState (); } public void removeUpdate(final javax.swing.event.DocumentEvent p0) { unmarkError (); } public void insertUpdate(final javax.swing.event.DocumentEvent p0) { unmarkError (); } public void changedUpdate(final javax.swing.event.DocumentEvent p0) { } } /** Abstract implementation of {@link Line.Set}. * Defines * ways to obtain a line set for documents following * NetBeans conventions. */ public static abstract class Set extends Line.Set { /** listener on document changes */ private LineListener listener; /** all lines in the set or null */ private java.util.List list; /** map to hold all existing lines (Line, Reference (Line)) */ private static WeakHashMap allLines = new WeakHashMap (37); /** Constructor. * @param doc document to work on */ public Set (StyledDocument doc) { listener = new LineListener (doc); } /* Returns an unmodifiable set of Lines sorted by their * line numbers that contains all lines holded by this * Line.Set. * * @return list of Line objects */ public java.util.List getLines () { if (list == null) { int cnt = listener.getOriginalLineCount (); java.util.List l = new java.util.LinkedList (); for (int i = 0; i < cnt; i++) { l.add (getOriginal (i)); } list = l; } return list; } /* Finder method that for the given line number finds right * Line object that represent as closely as possible the line number * in the time when the Line.Set has been created. * * @param line is a number of the line (text line) we want to acquire * @exception IndexOutOfBoundsException if <code>line</code> is invalid. */ public Line getOriginal (int line) throws IndexOutOfBoundsException { int newLine = listener.getLine (line); int offset = NbDocument.findLineOffset (listener.doc, newLine); // System.out.println("Then: " + line + " now: " + newLine + " offset: " + offset); // NOI18N Line ll = registerLine (createLine (offset)); return ll; } /* Creates current line. * * @param line is a number of the line (text line) we want to acquire * @exception IndexOutOfBoundsException if <code>line</code> is invalid. */ public Line getCurrent (int line) throws IndexOutOfBoundsException { int offset = NbDocument.findLineOffset (listener.doc, line); // System.out.println("Then: " + line + " now: " + newLine + " offset: " + offset); // NOI18N Line ll = registerLine (createLine (offset)); return ll; } /** Creates a {@link Line} for a given offset. * @param offset the beginning offset of the line * @return line object representing the line at this offset */ protected abstract Line createLine (int offset); /** Registers the line. * @param l line to register * @return the line l or line previously registered */ static synchronized Line registerLine (Line l) { Reference ref = (Reference)allLines.get (l); Line prev = ref == null ? null : (Line)ref.get (); if (prev == null) { if (l instanceof DocumentLine) ((DocumentLine)l).init (); allLines.put (l, new WeakReference (l)); return l; } else { return prev; } } } } /* * Log * 22 src-jtulach1.21 1/15/00 Daniel Prusa Line registration fixed * 21 src-jtulach1.20 1/14/00 Jaroslav Tulach Lines are really hold * weakly. * 20 src-jtulach1.19 1/13/00 Ian Formanek NOI18N * 19 src-jtulach1.18 11/5/99 Jaroslav Tulach WeakListener has now * registration methods. * 18 src-jtulach1.17 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 17 src-jtulach1.16 8/17/99 Ian Formanek Generated serial version * UID * 16 src-jtulach1.15 8/3/99 Jaroslav Tulach Project settings node. * 15 src-jtulach1.14 8/2/99 Jaroslav Tulach * 14 src-jtulach1.13 7/30/99 Jaroslav Tulach getOriginal & getCurrent * in Line * 13 src-jtulach1.12 7/27/99 Jaroslav Tulach Faster lines. * 12 src-jtulach1.11 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 11 src-jtulach1.10 4/9/99 David Simonek bugfix #1429 * 10 src-jtulach1.9 3/19/99 Jaroslav Tulach * 9 src-jtulach1.8 3/19/99 Jaroslav Tulach * 8 src-jtulach1.7 3/18/99 Jaroslav Tulach * 7 src-jtulach1.6 2/11/99 Jaroslav Tulach * 6 src-jtulach1.5 2/10/99 Jesse Glick [JavaDoc] * 5 src-jtulach1.4 2/10/99 Jesse Glick [JavaDoc] * 4 src-jtulach1.3 2/3/99 Jaroslav Tulach * 3 src-jtulach1.2 2/2/99 Jaroslav Tulach * 2 src-jtulach1.1 1/29/99 Jaroslav Tulach SortedSet changed to List * 1 src-jtulach1.0 1/29/99 Jaroslav Tulach * $ */